#pragma once
#include <algorithm>
#include <jsoncpp/json/json.h>
#include "index.hpp"
#include "log.hpp"

struct InvertedElemPrint
{
    uint64_t doc_id;
    int weight;
    std::vector<std::string> words;
    InvertedElemPrint() : doc_id(0), weight(0) {}
};

class Searcher
{
private:
    Index *index;  //供系统查找的索引
public:
    Searcher(){}
    ~Searcher(){}
public:
    void InitSearher(const std::string& input)
    {
        // 获取或者创建index对象
        index = Index::GetInstance();
        //std::cout << "获取index单例成功..." << std::endl;
        LOG(NORMAL, "获取index单例成功...");
        // 根据index对象建立索引
        index->BuildIndex(input);
        //std::cout << "建立正排和倒排索成功..." << std::endl;
        LOG(NORMAL, "建立正排和倒排索引成功...");
    }

    // query 搜索关键字
    // json_string 返回给用过户浏览器搜索结果
    void Search(const std::string& query, std::string* json_string)
    {
        // [分词] 对我们的query按照searcher的要求分词
        std::vector<std::string> words;
        JiebaUtil::CunString(query, &words);
        // [触发] 根据分词的各个词，进行index查找
        //InvertedList inverted_list_all;  // 内部 InvertedElem
        std::vector<InvertedElemPrint> inverted_list_all;
        std::unordered_map<uint64_t, InvertedElemPrint> tokens_map;

        for (std::string word : words)
        {
            boost::to_lower(word);
            InvertedList *inverted_list = index->GetInvertedList(word);
            if (inverted_list == nullptr)
            {
                continue;
            }

            // 不完善的地方, 你是一个好人
            //inverted_list_all.insert(inverted_list_all.end(), inverted_list->begin(), inverted_list->end());
            for(const auto &elem : *inverted_list){
                auto &item = tokens_map[elem.doc_id]; //[]:如果存在直接获取，如果不存在新建
                //item一定是doc_id相同的print节点
                item.doc_id = elem.doc_id;
                item.weight += elem.weight;
                item.words.push_back(elem.word);
            }
        }

        for(const auto &item : tokens_map){
            inverted_list_all.push_back(std::move(item.second));
        }

        // [合并排序] 汇总查找结果，按照相关性（weight）降序排序
        std::sort(inverted_list_all.begin(), inverted_list_all.end(),
                  [](const InvertedElemPrint &e1, const InvertedElemPrint &e2)
                  {
                      return e1.weight > e2.weight;
                  });
        // [构建] 根据查找出来的结果，构建json串  --jsoncpp
        Json::Value root;
        for (auto& item : inverted_list_all)
        {
            DocInfo *doc = index->GetForwardIndex(item.doc_id);
            if (doc == nullptr)
            {
                continue;
            }

            Json::Value elem;
            elem["title"] = doc->title;
            elem["desc"] = GetDesc(doc->content, item.words[0]);  // content是文档去标签的结果，不是我们想要的，只要一部分
            elem["url"] = doc->url;
            elem["id"] = (int)item.doc_id;
            elem["weigth"] = item.weight;
            root.append(elem);
        }

        //Json::StyledWriter writer;
        Json::FastWriter writer;
        *json_string = writer.write(root);
    }

    std::string GetDesc(const std::string &html_content, const std::string& word)
    {
        // 找到word在htmlcontent中首次出现，然后往前找50字节（如果没有，从begin开始）
        // 往后找100字节（如果没有，到end结束）
        // 截取这部分内容
        const std::size_t prev_step = 50;
        const std::size_t next_step = 50;
        // 找到首次出现
        auto iter = std::search(html_content.begin(), html_content.end(), word.begin(), word.end(),
                                [](int x, int y)
                                {
                                    return (std::tolower(x) == std::tolower(y));
                                });
        if (iter == html_content.end())
        {
            return "None1";  // 这种情况是不可能存在的
        }

        std::size_t pos = std::distance(html_content.begin(), iter);
        // 截取start，end
        int start = 0;
        int end = html_content.size() - 1; 
        // 如果之前有50字符，更新开始位置
        if (pos > start + prev_step)
            start = pos - prev_step;
        if (pos < end - next_step)
            end = pos + next_step;
        // 截取子串
        if (start >= end)
        {
            return "None2";
        }

        std::string desc = html_content.substr(start, end - start);
        desc += "...";
        return desc;
    }
};